package com.seed.core.builder.query;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import com.seed.core.builder.datapermis.DataPermisWrapper;
import com.seed.core.builder.declare.DeclaredWrapper;
import com.seed.core.builder.keyword.KeywordWrapper;
import com.seed.core.builder.order.OrderedWrapper;
import com.seed.core.builder.pojo.*;
import com.seed.core.builder.search.SearchedWrapper;
import com.seed.core.enumeration.Direction;
import com.seed.core.pojo.seed.Seed;
import com.seed.core.query.pojo.Declared;
import com.seed.core.query.pojo.Ordered;
import com.seed.core.query.pojo.Searched;
import com.seed.util.ConditionKit;
import com.seed.util.SeedKit;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class AbstractQueryBuilder {
    protected final Seed seed;
    protected DeclaredWrapper declaredWrapper;
    protected SearchedWrapper searchedWrapper;
    protected OrderedWrapper orderedWrapper;
    protected KeywordWrapper keywordWrapper;
    protected DataPermisWrapper dataPermisWrapper;

    public AbstractQueryBuilder(Seed seed) {
        this.seed = seed;
    }

    public QueryWrapper build() {
        QueryTable table = new QueryTable(seed.getTableName()).as("t");
        QueryWrapper qw = QueryWrapper.create().from(table);

        buildSelects(qw, table);
        buildJoins(qw);
        buildInBuilders(qw);
        buildWheres(qw);
        buildOrder(qw);

        return qw;
    }

    public void fills(List<Row> rootList) {
        if(declaredWrapper == null) return;

        for(Map.Entry<String, FillBuilder> entry: declaredWrapper.getFills().entrySet()) {
            String key = entry.getKey();
            FillBuilder builder = entry.getValue();
            Seed seed = builder.getSeed();
            if(searchedWrapper != null) {
                Searched searched = searchedWrapper.getFillSearched(key);
                if(searched != null) builder.searchedWrapper(new SearchedWrapper(seed, searched));
            }
            if(keywordWrapper != null) {
                String keyword = keywordWrapper.getKeyword();
                Declared declared = keywordWrapper.getFillKeyworded(key);
                if(StrUtil.isNotBlank(keyword) && declared != null) {
                    builder.keywordWrapper(new KeywordWrapper(seed, declared, keyword));
                }
            }
            if(dataPermisWrapper != null) {
                List<Map<String, Searched>> dataPermied = dataPermisWrapper.getFillDataPermied();
                if(dataPermied != null) builder.dataPermisWrapper(new DataPermisWrapper(seed, dataPermied));
            }
            if(orderedWrapper != null) {
                Ordered ordered = orderedWrapper.getFillOrdered(key);
                if(ordered != null) builder.orderedWrapper(new OrderedWrapper(seed, ordered));
            }

            entry.getValue().fill(rootList);
        }
    }

    protected void buildSelects(QueryWrapper qw, QueryTable table) {
        if(declaredWrapper == null || declaredWrapper.getSelects().isEmpty()) {
            for(String columnName: SeedKit.getDefaultDeclares(seed)) {
                qw.select(new QueryColumn(table, columnName));
            }
        } else {
            for(Column1 column: declaredWrapper.getSelects().values()) {
                Table1 t = column.getTable();
                QueryTable queryTable = new QueryTable(t.getName()).as(t.getAlias());
                qw.select(new QueryColumn(queryTable, column.getName(), column.getAlias()));
            }
        }
    }

    protected void buildJoins(QueryWrapper qw) {
        Map<String, Join1> joins = new HashMap<>();
        for(Map.Entry<String, Join1> entry: declaredWrapper.getJoins().entrySet()) {
            joins.putIfAbsent(entry.getKey(), entry.getValue());
        }
        if(searchedWrapper != null) {
            for(Map.Entry<String, Join1> entry: searchedWrapper.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(keywordWrapper != null) {
            for(Map.Entry<String, Join1> entry: keywordWrapper.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(orderedWrapper != null) {
            for(Map.Entry<String, Join1> entry: orderedWrapper.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(dataPermisWrapper != null) {
            for(Map.Entry<String, Join1> entry: dataPermisWrapper.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }

        for(Join1 join: joins.values()) {
            Table1 table = join.getTable();
            QueryTable queryTable = new QueryTable(table.getName(), table.getAlias());
            switch (join.getJoinType()) {
                case inner -> qw.innerJoin(queryTable).on(ConditionKit.build(join.getOn()));
                case left -> qw.leftJoin(queryTable).on(ConditionKit.build(join.getOn()));
                case right -> qw.rightJoin(queryTable).on(ConditionKit.build(join.getOn()));
            }
        }
    }

    protected void buildInBuilders(QueryWrapper qw) {
        Map<String, InBuilder> inBuilders = new LinkedHashMap<>();
        if(searchedWrapper != null) inBuilders.putAll(searchedWrapper.getInBuilders());
        if(keywordWrapper != null) {
            for(Map.Entry<String, InBuilder> entry: keywordWrapper.getInBuilders().entrySet()) {
                String key = entry.getKey();
                InBuilder value = entry.getValue();
                InBuilder inBuilder = inBuilders.get(key);
                if(inBuilder == null) {
                    inBuilders.put(key, value);
                } else {
                    inBuilder.setDeclared(value.getDeclared());
                    inBuilder.setKeyword(keywordWrapper.getKeyword());
                }
            }
        }
        if(dataPermisWrapper != null) {
            for(Map.Entry<String, InBuilder> entry: dataPermisWrapper.getInBuilders().entrySet()) {
                String key = entry.getKey();
                InBuilder value = entry.getValue();
                InBuilder inBuilder = inBuilders.get(key);
                if(inBuilder == null) {
                    inBuilders.put(key, value);
                } else {
                    inBuilder.setDataPermied(value.getDataPermied());
                }
            }
        }

        for(Map.Entry<String, InBuilder> entry: inBuilders.entrySet()) {
            qw.and(entry.getValue().getQueryCondition());
        }
    }

    protected void buildWheres(QueryWrapper qw) {
        if(searchedWrapper != null) {
            for(WhereCondition1 condition: searchedWrapper.getWheres()) {
                qw.and(ConditionKit.build(condition));
            }
        }

        if(keywordWrapper != null) {
            qw.and(queryWrapper -> {
                for(WhereCondition1 condition: keywordWrapper.getWheres()) {
                    queryWrapper.or(ConditionKit.build(condition));
                }
            });
        }

        if(dataPermisWrapper != null) {
            QueryCondition dataPermisCondition = QueryCondition.createEmpty();
            List<List<WhereCondition1>> wheres = dataPermisWrapper.getWheres();
            for(List<WhereCondition1> list: wheres) {
                QueryCondition selfCondition = QueryCondition.createEmpty();
                for(WhereCondition1 condition: list) selfCondition.and(ConditionKit.build(condition));
                dataPermisCondition.or(selfCondition);
            }

            qw.and(dataPermisCondition);
        }
    }

    protected void buildOrder(QueryWrapper qw) {
        if(orderedWrapper != null) {
            for(OrderBy1 orderBy: orderedWrapper.getOrders().values()) {
                Table1 table = orderBy.getTable();
                QueryTable queryTable = new QueryTable(table.getName()).as(table.getAlias());
                QueryColumn queryColumn = new QueryColumn(queryTable, orderBy.getColumn());
                qw.orderBy(queryColumn, orderBy.getDirection().equals(Direction.asc));
            }
        }
    }

    public Seed getSeed() {
        return seed;
    }

    public AbstractQueryBuilder declaredWrapper(DeclaredWrapper declaredWrapper) {
        this.declaredWrapper = declaredWrapper;
        return this;
    }

    public AbstractQueryBuilder searchedWrapper(SearchedWrapper searchedWrapper) {
        this.searchedWrapper = searchedWrapper;
        return this;
    }

    public AbstractQueryBuilder orderedWrapper(OrderedWrapper orderedWrapper) {
        this.orderedWrapper = orderedWrapper;
        return this;
    }

    public AbstractQueryBuilder keywordWrapper(KeywordWrapper keywordWrapper) {
        this.keywordWrapper = keywordWrapper;
        return this;
    }

    public AbstractQueryBuilder dataPermisWrapper(DataPermisWrapper dataPermisWrapper) {
        this.dataPermisWrapper = dataPermisWrapper;
        return this;
    }
}
